package info.niwota.ezb;

import java.util.Arrays;
import java.util.List;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.webkit.WebView;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ZoomButtonsController;

public class ImageHolderActivity extends Activity implements
		View.OnClickListener {

	protected boolean scrollLocked;

	// 
	private class MyGestureListener extends
			GestureDetector.SimpleOnGestureListener {

		@Override
		public boolean onDoubleTap(MotionEvent e) {
			if (scrollLocked) {
				return true;
			}
			ImageViewTouch iv = mImageView;
			// save scale if greater than 1.0f or zoom and center at the tap
			// point
			if (iv.getScale() > 1.0f) {
				saveScaleIf(iv.getScale());
				//
				iv.zoomTo(1f);
			} else {
				float scale = Preferences.getScale(ImageHolderActivity.this);
				iv.zoomToPoint(scale, e.getX(), e.getY());
			}
			return true;
		}

		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2,
				float distanceX, float distanceY) {
			if (scrollLocked) {
				return true;
			}
			ImageViewTouch iv = mImageView;
			if (iv.getScale() > 1.0f) {
				iv.postTranslateCenter(-distanceX, -distanceY);
			}
			return true;
		}

		@Override
		public boolean onSingleTapConfirmed(MotionEvent e) {
			//only show screen saver button if scrolling is locked
			if (scrollLocked) {
				showScrollLock();
				return true;
			}
			showOnScreenControls();
			scheduleDismissOnScreenControls();
			return true;
		}

		@Override
		public void onLongPress(MotionEvent e) {
			if (mScaleGestureDetector != null && mScaleGestureDetector.isInProgress()) {
				return; //ignore
			}
			toggleScrollLock();
		}

		@Override
		public boolean onSingleTapUp(MotionEvent e) {
			return true;
		}
	}

	private static final int DIALOG_HELP = 10;

	private static final String HELP_URI = "file:///android_asset/help.html";

	/* package */static final int RC_PICK = 1;

	private static final int RC_SEND = 2;

	private static final int RC_SETTINGS = 3;

	//private static final int RC_SAVE_SCREEN = 4;

	private static final String TAG = "ImageHolderActivity";

	private static final int DIALOG_PICK = 12;

	private static final int DIALOG_REMOVE = 11;

	private static final String EXTRA_URIS =  "extra.uris";

	private View mActionIconPanel;

	//
	ImageList mAllImages;

	//
	int mCurrentPosition = 0;

	protected Runnable mDeleteImageRunnable = new Runnable() {
		public void run() {
			mAllImages.removeImageAt(mCurrentPosition);
			int cnt = mAllImages.getCount();
			if (cnt == 0) {
				showBlank();
				return;
			} else {
				if (mCurrentPosition == cnt) {
					mCurrentPosition -= 1;
				}
			}
			mImageView.clear();
			setImage(mCurrentPosition);
		}
	};

	private final Runnable mDismissOnScreenControlRunner = new Runnable() {
		public void run() {
			hideOnScreenControls();
		}
	};

	private GestureDetector mGestureDetector;

	private ScaleGestureDetector mScaleGestureDetector;

	private ImageGetter mGetter;

	final GetterHandler mHandler = new GetterHandler();
	private final Animation mHideNextImageViewAnimation = new AlphaAnimation(
			1F, 0F);
	private final Animation mHidePrevImageViewAnimation = new AlphaAnimation(
			1F, 0F);

	private ImageViewTouch mImageView;

	private View mNextImageView;

	boolean mPaused = true;

	private View mPrevImageView;

	private final Animation mShowNextImageViewAnimation = new AlphaAnimation(
			0F, 1F);

	private final Animation mShowPrevImageViewAnimation = new AlphaAnimation(
			0F, 1F);

	private TextView mTextView;

	private ZoomButtonsController mZoomButtonsController;

	protected ImageButton mSaveScreen;

	private ImageButton mLockScroll;

	private Dialog createHelpDialog() {
		LayoutInflater f = LayoutInflater.from(this);
		final View v = f.inflate(R.layout.webdia, null);

		Dialog d = new Dialog(this);
		LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
		d.requestWindowFeature(Window.FEATURE_LEFT_ICON);
		d.setContentView(v, lp);
		d.setTitle(R.string.title_help_text);
		d.setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, R.drawable.icon);

		return d;
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent m) {
		if (mPaused) {
			return true;
		}
		if (mZoomButtonsController.isVisible()) {
			scheduleDismissOnScreenControls();
		}
		return super.dispatchTouchEvent(m);
	}

	private void doAdd(Intent data) {
		try {
			Uri uri = data.getData();
			if (Debug.DEBUG) {
				Log.d(TAG, "doAdd uri: " + uri);
			}
			// multi select
			List<Uri> list = data
					.getParcelableArrayListExtra(EXTRA_URIS);
			if (Debug.DEBUG) {
				Log.d(TAG, "doAdd list: " + list);
			}
			//
			initList();
			//
			if (list != null) {
				for (Uri i : list) {
					mAllImages
							.add(this.getContentResolver(), mCurrentPosition, i);
				}
			} else {
				if (uri != null) {
					mAllImages.add(this.getContentResolver(), mCurrentPosition, uri);
				}
			}
			
			//
			Toast.makeText(this, R.string.text_added, Toast.LENGTH_SHORT)
					.show();
			//
			//
			mImageView.clear();
			setImage(mCurrentPosition);
		} catch (Exception e) {
			showError(e);
			if (Debug.DEBUG) {
				e.printStackTrace();
			}
		}
	}

	protected void doPick() {
		try {
			if (!WebshotInstallActivity.isWebshotAvailable(this)) {
				showDialog(DIALOG_PICK);
			} else {
				Intent i = createContentIntent();
				final CharSequence title = getText(R.string.text_pick);
				Intent ci = Intent.createChooser(i, title);
				startActivityForResult(ci, RC_PICK);
			}
		} catch (Exception e) {
			showError(e);
			e.printStackTrace();
		}
	}

	private Intent createContentIntent() {
		Intent i = new Intent(Intent.ACTION_GET_CONTENT);
		i.setType("image/*");
		return i;
	}

	/* package */void doRemove() {
		showDialog(DIALOG_REMOVE);
	}

	/* package */void doSaveScale(float scale) {
		// force save regardless of the scale value
		Preferences.putScale(this, scale);
		boolean scaled = (scale > 1.0f);
		Preferences.putScaled(this, scaled);
		//
		Toast.makeText(this, R.string.text_scale_saved, Toast.LENGTH_SHORT)
				.show();
	}
	
	private void doCallTaxi() {
		try {
			Context ctx = this;
			String phone = Preferences.getPhone(ctx);
			
			if (TextUtils.isEmpty(phone)) {
				Intent i = new Intent(Intent.ACTION_DIAL);
				ctx.startActivity(i);
			} else {
				Intent i = new Intent(Intent.ACTION_CALL, Uri.fromParts("tel", phone, null));
				ctx.startActivity(i);
			}
		} catch (Exception e) {
			if (Debug.DEBUG) {
				e.printStackTrace();
			}
			showError(e);
		}
	}

	private void doSettings() {
		try {
			Intent i = new Intent(this, SettingsActivity.class);
			startActivityForResult(i, RC_SETTINGS);
		} catch (Exception e) {
			// e.printStackTrace();
		}
	}

	protected void doShare() {
		IImage image = mAllImages.getImageAt(mCurrentPosition);
		if (image != null) {
			startSendActivity(image);
		}
	}

	private void hideOnScreenControls() {
		View panel = mActionIconPanel;
		//
		if (panel.getVisibility() == View.VISIBLE) {
			Animation a = new AlphaAnimation(1, 0);
			a.setDuration(500);
			panel.startAnimation(a);
			panel.setVisibility(View.INVISIBLE);
		}

		if (mNextImageView.getVisibility() == View.VISIBLE) {
			Animation a = mHideNextImageViewAnimation;
			a.setDuration(500);
			mNextImageView.startAnimation(a);
			mNextImageView.setVisibility(View.GONE); //INVISIBLE
		}

		if (mPrevImageView.getVisibility() == View.VISIBLE) {
			Animation a = mHidePrevImageViewAnimation;
			a.setDuration(500);
			mPrevImageView.startAnimation(a);
			mPrevImageView.setVisibility(View.GONE); //INVISIBLE
		}

		mZoomButtonsController.setVisible(false);
		boolean b = hasImage();
		if (b) {
			mTextView.setVisibility(View.GONE);
		} else {
			showEmptyText();
		}

		mSaveScreen.setVisibility(View.GONE);
		mLockScroll.setVisibility(View.GONE);
	}

	private void init() {
		if (Debug.DEBUG) {
			Log.d(TAG, "init ");
		}
		initList();
		//
		mSaveScreen = (ImageButton) findViewById(R.id.action_save_screen);
		mSaveScreen.setOnClickListener(this);
		
		mLockScroll = (ImageButton) findViewById(R.id.action_lock_scroll);
		mLockScroll.setOnClickListener(this);

		mTextView = (TextView) findViewById(R.id.text);
		mImageView = (ImageViewTouch) findViewById(R.id.image);
		mActionIconPanel = findViewById(R.id.action_icon_panel);
		View panel = mActionIconPanel;
		//
		boolean kb = isKeyguarded();
		panel.setVisibility(View.VISIBLE);//panel.setVisibility(kb ? View.INVISIBLE : View.VISIBLE);
		int vi = (kb ? View.GONE : View.VISIBLE);
		//
		int[] ids = { R.id.add, R.id.share, R.id.remove };
		for (int id : ids) {
			View view = panel.findViewById(id);
			view.setVisibility(vi); //view.setVisibility(View.VISIBLE);
			view.setOnClickListener(this);
		}
		mImageView.setEnableTrackballScroll(true);
		//
		setupOnScreenControls(findViewById(R.id.rootLayout), mImageView);
		//
		mPaused = false;
		//
		mGetter = new ImageGetter();
		//
		int cnt = mAllImages.getCount();
		if (cnt == 0) {
			mCurrentPosition = 0;
			//
			showBlank();
			return;
		} else if (cnt <= mCurrentPosition) {
			mCurrentPosition = cnt - 1;
		}
		setImage(mCurrentPosition);
	}

	protected boolean isKeyguarded() {
		return (KeyguardUtil.isKeyguarded(this));
	}

	private void initList() {
		if (Debug.DEBUG) {
			Log.d(TAG, "initList " + mAllImages);
		}
		if (mAllImages == null) {
			mAllImages = new ImageList();
			mAllImages.restore(this);
			mCurrentPosition = Preferences.getPos(this);
			//
			if (Debug.DEBUG) {
				Log.d(TAG, "initList " + mCurrentPosition + ":" + mAllImages);
			}
		}
	}

	private void moveNextOrPrevious(int delta) {
		int nextImagePos = mCurrentPosition + delta;
		if ((0 <= nextImagePos) && (nextImagePos < mAllImages.getCount())) {
			resetScaled();
			//
			setImage(nextImagePos);
			showOnScreenControls();
		}
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onActivityResult requestCode: " + requestCode
					+ " resultCode:" + resultCode + " == " + RESULT_OK
					+ " data: " + data);
		}
		if (resultCode == RESULT_OK) {
			switch(requestCode) {
			case RC_SETTINGS:
//				final Context ctx = this;
//				if (Preferences.enableStatusbar(ctx)) {
//					Notifications.addNotification(ctx);
//				} else {
//					Notifications.removeNotification(ctx);
//				}
				final Context ctx = this;
				if (Preferences.enablePowerFeature(ctx)) {
					ScreenService.startup(ctx);
				} else {
					ScreenService.shutdown(ctx);
				}
				break;
			case RC_PICK:
				doAdd(data);
				break;
			}
		}
	}

	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.add:
			doPick();
			break;
		case R.id.remove:
			doRemove();
			break;
		case R.id.share:
			doShare();
			break;
		case R.id.next_image:
			moveNextOrPrevious(1);
			break;
		case R.id.prev_image:
			moveNextOrPrevious(-1);
			break;
		case R.id.action_save_screen:
			doSaveScreen();
			break;
		case R.id.action_lock_scroll:
			toggleScrollLock();
			break;
		}
	}

	protected void doSaveScreen() {
		Screens.blackscreen(this);
	}

	private void toggleScrollLock() {
		scrollLocked = (!scrollLocked);
		mImageView.setEnableTrackballScroll(!scrollLocked);
		if (Debug.DEBUG) {
			Log.d(TAG, "onLongPress scrollLocked: " + scrollLocked);
		}
		Context ctx = this;
		int msg = (scrollLocked? R.string.text_scroll_locked : R.string.text_scroll_unlocked);
		Toast.makeText(ctx, msg, Toast.LENGTH_SHORT).show();
	}

	@Override
	public void onCreate(Bundle instanceState) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onCreate " + instanceState);
		}
		setFullscreen();
		
		super.onCreate(instanceState);

		//requestWindowFeature(Window.FEATURE_NO_TITLE);

		setContentView(R.layout.imageholder);

		//getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		
		//
		//WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
		//WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
		//WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
		//WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
		//
		addPassIf(getIntent());
		
		setRunningStatus(true);
		
		//initSensor();
	}

	private IntentFilter intentFilter;
	private BroadcastReceiver receiver = new BroadcastReceiver() {

		public void onReceive(Context ctx, Intent intent) {
			final String action = intent.getAction();

			if (Debug.DEBUG) {
				Log.d(TAG, "action: " + action + " paused: " + mPaused);
			}

			if (PhoneUtil.ACTION_PHONE_STATE.equals(action)) {
				final String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
				
				if (Debug.DEBUG) {
					Log.d(TAG, "phone state change, state: " + state + " extras: " + intent.getExtras());
				}
				
				if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)) {
					//update
					final boolean active = !mPaused;
					ApplicationContext.setActive(active);
					
					boolean b = moveTaskToBack(true);

					if (Debug.DEBUG) {
						Log.d(TAG, "phone state change, moved to bg: " + b + " active: " + active);
					}
				} 
				return;
			}
			
			if (Intent.ACTION_SCREEN_OFF.equals(action)) {
				boolean b = moveTaskToBack(true);
				
				if (Debug.DEBUG) {
					Log.d(TAG, "screen off, moved to bg: " + b);
				}
			}
		}
	};

	protected void setRunningStatus(boolean b) {
		//ezb is considered running until explicitly exited
		final Context ctx = this;
		if (b) {
			intentFilter = new IntentFilter();
			intentFilter.addAction(Intent.ACTION_SCREEN_ON);
			intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
			intentFilter.addAction(PhoneUtil.ACTION_PHONE_STATE);
			
			registerReceiver(receiver, intentFilter);
			//
			Preferences.updateRunningStatus(ctx, true);
			Notifications.addNotification(ctx);
			
			if (Preferences.enablePowerFeature(ctx)) {
				ScreenService.startup(this);
			}
		} else {
			try {
				unregisterReceiver(receiver);
			} catch (Exception e) {
				e.printStackTrace();
			}
			//
			Preferences.updateRunningStatus(ctx, false);
			Notifications.removeNotification(ctx);
			
			ScreenService.shutdown(ctx);
		}
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		switch (id) {
		case DIALOG_HELP:
			return createHelpDialog();
		case DIALOG_REMOVE:
			return createRemoveDialog();
		case DIALOG_PICK:
			return createPickDialog();
		default:
		}
		return super.onCreateDialog(id);
	}

	private Dialog createRemoveDialog() {
		OnClickListener cl = new OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				switch (which) {
				case DialogInterface.BUTTON_POSITIVE:
					//
					resetScaled();
					mDeleteImageRunnable.run();
					break;
				default:
				}
			}
		};
		AlertDialog.Builder b = new AlertDialog.Builder(this);
		b.setIcon(android.R.drawable.ic_dialog_alert);
		b.setTitle(R.string.action_remove);
		b.setMessage(R.string.text_remove);
		b.setPositiveButton(android.R.string.ok, cl);
		b.setNegativeButton(android.R.string.cancel, cl);
		//
		return b.create();
	}

	private void resetScaled() {
		Preferences.putScaled(this, false);
	}

	private Dialog createPickDialog() {
		Intent i = createContentIntent();
		return PickDialog.create(this, i, R.string.text_pick);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onCreateOptionsMenu: " + menu);
		}
		//
		MenuInflater mif = getMenuInflater();
		mif.inflate(R.menu.imageholder_options, menu);

		return true;
	}

	@Override
	protected void onDestroy() {
		if (Debug.DEBUG) {
			Log.d(TAG, "onDestroy");
		}
		// This is necessary to make the ZoomButtonsController unregister
		// its configuration change receiver.
		ZoomButtonsController c = mZoomButtonsController;
		if (c != null) {
			c.setVisible(false);
		}
		
		//
		super.onDestroy();
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		// seems not working with screen on lock
		if (Debug.DEBUG) {
			Log.d(TAG, "onConfigurationChanged ignored " + newConfig);
		}
		//android.app.SuperNotCalledException:
		super.onConfigurationChanged(newConfig);
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onKeyDown " + keyCode + " event: " + event + " keyguard: " + isKeyguarded());
		}
		if (scrollLocked) {
			showScrollLock();
			return true;
		}
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			//disable back key
			if (isKeyguarded()) {
				return true;
			}
			
			doQuit();
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}

	protected void doQuit() {
		if (Debug.DEBUG) {
			Log.d(TAG, "doQuit");
		}
		finish();
	}

	@Override
	public boolean onMenuItemSelected(int featureId, MenuItem item) {
		boolean b = super.onMenuItemSelected(featureId, item);
		return b;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onOptionsItemSelected: " + item);
		}
		int id = item.getItemId();
		switch (id) {
		case R.id.imageholder_add_opt_menu_id:
			doPick();
			return true;
		case R.id.imageholder_remove_opt_menu_id:
			doRemove();
			return true;
		case R.id.imageholder_send_opt_menu_id:
			doShare();
			return true;
		case R.id.imageholder_help_opt_menu_id:
			showDialog(DIALOG_HELP);
			return true;
		case R.id.imageholder_settings_opt_menu_id:
			doSettings();
			return true;
		case R.id.imageholder_quit_opt_menu_id:
			doFinish();
			return true;
		case R.id.imageholder_call_opt_menu_id:
			doCallTaxi();
			return true;
		default:
		}
		return false;
	}

	private void doFinish() {
		if (Debug.DEBUG) {
			Log.d(TAG, "doFinish");
		}

		//
		setRunningStatus(false);
		//
		doQuit();
	}

	@Override
	protected void onPause() {
		if (Debug.DEBUG) {
			Log.d(TAG, "onPause");
		}
		
		try {
			//
			releaseLock();
			//
			ImageList l = mAllImages;
			if (l != null) {
				l.save(this);
			}
			Preferences.putPos(this, mCurrentPosition);
			//
			super.onPause();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void onOptionsMenuClosed(Menu menu) {
		super.onOptionsMenuClosed(menu);
	}

	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {
		Issues.fixIssue3674(dialog.getWindow());
		
		switch(id) {
		case DIALOG_HELP:
			WebView wv = (WebView) dialog.findViewById(R.id.webview);
			wv.loadUrl(HELP_URI);
			wv.setFocusable(true);
			wv.setFocusableInTouchMode(true);
			break;
		}

		super.onPrepareDialog(id, dialog);
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		super.onPrepareOptionsMenu(menu);
		if (mPaused) {
			return false;
		}
		//
		if (scrollLocked) {
			showScrollLock();
			return false;
		}
		
		//no options menu if keyguuard
		boolean kb = isKeyguarded();
		if (Debug.DEBUG) {
			Log.d(TAG, "isKeyguarded: " + kb);
		}
		if (kb) {
			return false;
		}
		//
		MenuItem item = null;
		boolean b = (mAllImages != null && mAllImages.getCount() > 0);
		//
		item = menu.findItem(R.id.imageholder_send_opt_menu_id);
		if (item != null) {
			item.setEnabled(b);
		}
		item = menu.findItem(R.id.imageholder_remove_opt_menu_id);
		if (item != null) {
			item.setEnabled(b);
		}
		//
		return true;
	}

	protected void showScrollLock() {
		mLockScroll.setVisibility(View.VISIBLE);
		scheduleDismissOnScreenControls();
	}

	@Override
	protected void onRestoreInstanceState(Bundle b) {
		super.onRestoreInstanceState(b);
		//
		if (Debug.DEBUG) {
			Log.d(TAG, "onRestoreInstanceState bundle: " + b);
		}
	}

	@Override
	protected void onResume() {
		if (Debug.DEBUG) {
			Log.d(TAG, "onResume");
		}
		//
		try {
			super.onResume();
			//
			if (Preferences.enableFullbright(this)) {
				Screens.setFullBrightness(this);
			} else {
				Screens.setDefaultBrightness(this);
			}
			//
			acquireLock();
			//
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

	@Override
	public Object onRetainNonConfigurationInstance() {
		if (Debug.DEBUG) {
			Log.d(TAG, "onRetainNonConfigurationInstance");
		}
		return null;
	}

	@Override
	public void onSaveInstanceState(Bundle b) {
		// IImage i = mAllImages.getImageAt(mCurrentPosition);
		// if (i != null) {
		// b.putParcelable(STATE_URI, i.fullSizeImageUri());
		// }
		//
		if (Debug.DEBUG) {
			Log.d(TAG, "onSaveInstanceState bundle: " + b);
		}
		super.onSaveInstanceState(b);
	}

	@Override
	public void onStart() {
		if (Debug.DEBUG) {
			Log.d(TAG, "onStart");
		}
		super.onStart();
		//
		init();
	}

	@Override
	protected void onRestart() {
		if (Debug.DEBUG) {
			Log.d(TAG, "onRestart");
		}
		super.onRestart();
		//
	}

	@Override
	protected void onNewIntent(Intent intent) {
		if (Debug.DEBUG) {
			Log.d(TAG, "onNewIntent " + intent);
		}
		super.onNewIntent(intent);
		//
		addPassIf(intent);
	}

	private void addPassIf(Intent intent) {
		try {
			if (intent == null) {
				return;
			}
			Uri data = intent.getData();
			if (Debug.DEBUG) {
				Log.d(TAG, "addPassIf uri: " + data + " extras: "
						+ intent.getExtras());
			}
			if (data != null) {
				Preferences.addUri(this, data.toString());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void onStop() {
		if (Debug.DEBUG) {
			Log.d(TAG, "onStop");
		}
		super.onStop();
		//
		try {
			// TODO may not be called
			//
			saveImage();
			//
			mPaused = true;
			// mGetter could be null if we call finish() and leave early in
			// onStart().
			if (mGetter != null) {
				mGetter.cancelCurrent();
				mGetter.stop();
				mGetter = null;
			}

			// removing all callback in the message queue
			mHandler.removeAllGetterCallbacks();

			if (mAllImages != null) {
				mAllImages.close();
				mAllImages = null;
			}

			hideOnScreenControls();
			mImageView.clear();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void saveScaleIf(float scale) {
		// save if scale is greater than 1.0f
		if (Debug.DEBUG) {
			Log.d(TAG, "saveScaleIf: " + scale);
		}
		boolean scaled = (scale > 1.0f);
		if (scaled) {
			Preferences.putScale(this, scale);
		}
		Preferences.putScaled(this, scaled);
	}

	private void saveImage() {
		// save settings
		ImageViewTouch iv = mImageView;
		float scale = iv.getScale();
		float[] values = iv.getMatrix();
		//
		saveScaleIf(scale);
		//
		Preferences.putScrollXY(this, values);
	}

	private void scheduleDismissOnScreenControls() {
		mHandler.removeCallbacks(mDismissOnScreenControlRunner);
		mHandler.postDelayed(mDismissOnScreenControlRunner, 2000);
	}

	/* package */void setImage(int pos) {
		if (Debug.DEBUG) {
			Log.d(TAG, "setImage pos: " + pos);
		}
		mCurrentPosition = pos;

		ImageGetterCallback cb = new ImageGetterCallback() {
			public void completed() {
			}

			//
			public int fullImageSizeToUse(int pos, int offset) {
				// this number should be bigger so that we can zoom. we may
				// need to get fancier and read in the fuller size image as the
				// user starts to zoom.
				// Originally the value is set to 480 in order to avoid OOM.
				// Now we set it to 2048 because of using
				// native memory allocation for Bitmaps.
				final int imageViewSize = 2048;
				return imageViewSize;
			}

			//
			public void imageLoaded(int pos, int offset, RotateBitmap bitmap,
					boolean isThumb) {
				//called twice thumb and full
				if (Debug.DEBUG) {
					Log.d(TAG, "imageLoaded current pos: " + mCurrentPosition  + " pos: " + pos + " offset: "+ offset+ " bitmap: " + bitmap +" isThumb: " + isThumb);
				}
				// shouldn't get here after onPause()
				//
				// We may get a result from a previous request. Ignore it.
				if (pos != mCurrentPosition) {
					bitmap.recycle();
					return;
				}

				if (offset == 0) {
					mImageView.setImageRotateBitmapResetBase(bitmap, isThumb);
					updateZoomButtonsEnabled();
					//
					restoreImage(mImageView, isThumb);
				}
			}
			
            public boolean wantsThumbnail(int pos, int offset) {
                return true;
            }
			//
			public boolean wantsFullImage(int pos, int offset) {
				return (offset == 0);
			}

			public int[] loadOrder() {
				return new int[] {0} ; //{0, 1, -1}
			}
		};

		if (mGetter != null) {
			mGetter.setPosition(pos, cb, mAllImages, mHandler);
		}

		//updateActionIcons();

		showOnScreenControls();

		scheduleDismissOnScreenControls();
	}

	private void restoreImage(ImageViewTouch iv, boolean isThumb) {
		if (Debug.DEBUG) {
			Log.d(TAG, "restoreImage isThumb:" + isThumb);
		}
		Context ctx = this;
		boolean scaled = Preferences.isScaled(ctx);
		if (Debug.DEBUG) {
			Log.d(TAG, "scaled: " + scaled);
		}
		//
		if (scaled && iv != null) {
			float scale = Preferences.getScale(ctx);
			float[] matrix = Preferences.getScrollXY(ctx);
			if (Debug.DEBUG) {
				Log.d(TAG, "scale: " + scale + " matrix: "
						+ Arrays.asList(matrix));
			}
			iv.restoreImage(scale, matrix);
		}
	}

	private void setupOnScreenControls(View rootView, View ownerView) {
		mNextImageView = rootView.findViewById(R.id.next_image);
		mPrevImageView = rootView.findViewById(R.id.prev_image);

		mNextImageView.setOnClickListener(this);
		mPrevImageView.setOnClickListener(this);

		setupZoomButtonController(ownerView);
		setupOnTouchListeners(rootView);
	}

	private void setupOnTouchListeners(View rootView) {
		mGestureDetector = new GestureDetector(this, new MyGestureListener());
		mScaleGestureDetector = new ScaleGestureDetector(this, new MyScaleGestureListener());
		
		// If the user touches anywhere on the panel (including the
		// next/prev button). We show the on-screen controls. In addition
		// to that, if the touch is not on the prev/next button, we
		// pass the event to the gesture detector to detect double tap.
		final OnTouchListener buttonListener = new OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				scheduleDismissOnScreenControls();
				return false;
			}
		};

		OnTouchListener rootListener = new OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				buttonListener.onTouch(v, event);
				mGestureDetector.onTouchEvent(event);
				mScaleGestureDetector.onTouchEvent(event);
				// We do not use the return value of
				// mGestureDetector.onTouchEvent because we will not receive
				// the "up" event if we return false for the "down" event.
				return true;
			}
		};

		mNextImageView.setOnTouchListener(buttonListener);
		mPrevImageView.setOnTouchListener(buttonListener);
		rootView.setOnTouchListener(rootListener);
	}

	private void setupZoomButtonController(final View ownerView) {
		final ZoomButtonsController c = new ZoomButtonsController(ownerView);
		c.setAutoDismissed(false);
		c.setZoomSpeed(100);
		c.setOnZoomListener(new ZoomButtonsController.OnZoomListener() {
			public void onVisibilityChanged(boolean visible) {
				if (visible) {
					updateZoomButtonsEnabled();
				}
			}

			public void onZoom(boolean zoomIn) {
				if (zoomIn) {
					mImageView.zoomIn();
				} else {
					mImageView.zoomOut();
				}
				c.setVisible(true);
				updateZoomButtonsEnabled();
			}
		});
		mZoomButtonsController = c;
	}

	private void showBlank() {
		mImageView.clear();
		//
		updateActionIcons();
		//
		showEmptyText();
		//
		mPrevImageView.setVisibility(View.GONE);
		mNextImageView.setVisibility(View.GONE);
		//
		mSaveScreen.setVisibility(View.GONE);
		mLockScroll.setVisibility(View.GONE);
	}

	private void showEmptyText() {
		if (mTextView == null) {
			return;
		}
		mTextView.setTextSize(16);
		mTextView.setVisibility(View.VISIBLE);
		mTextView.setText(R.string.text_page_info_none);
	}

	protected void showOnScreenControls() {
		if (mPaused) {
			return;
		}
		// If the view has not been attached to the window yet, the
		// zoomButtonControls will not able to show up. So delay it until the
		// view has attached to window.
		View panel = mActionIconPanel;
		if (panel.getWindowToken() == null) {
			mHandler.postGetterCallback(new Runnable() {
				public void run() {
					showOnScreenControls();
				}
			});
			return;
		}
		
		updateActionIcons();
		//
		updateNextPrevControls();

		updateZoomButtonsEnabled();
		//
		boolean b = hasImage();
		mZoomButtonsController.setVisible(b);
		
		//pagination
		if (b) {
			mTextView.setTextSize(28);
			String pi = getString(R.string.text_page_info,
					mCurrentPosition + 1, mAllImages.getCount());
			mTextView.setText(pi);
			mTextView.setVisibility(View.VISIBLE);
		} else {
			showEmptyText();
		}
		//final boolean kb = (KeyguardUtil.isKeyguarded(this));
		//
		//final int vi = (!kb && b ? View.VISIBLE : View.GONE);
		final int vi = (b ? View.VISIBLE : View.GONE);
		mSaveScreen.setVisibility(vi);
		mLockScroll.setVisibility(vi);
		//
		
		//panel.setVisibility(kb ? View.INVISIBLE : View.VISIBLE);
		//if (kb == false && panel.getVisibility() != View.VISIBLE) {
		if (panel.getVisibility() != View.VISIBLE) {
			Animation animation = new AlphaAnimation(0, 1);
			animation.setDuration(500);
			panel.startAnimation(animation);
			panel.setVisibility(View.VISIBLE);
		}
	}

	private void startSendActivity(IImage image) {
		try {
			Intent i = new Intent(this, CropImage.class);
			i.setAction(Intent.ACTION_SEND);
			i.setType(image.getMimeType());
			i.setData(image.fullSizeImageUri());
			//
			startActivityForResult(i, RC_SEND);
		} catch (android.content.ActivityNotFoundException e) {
			Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
		}
	}

	private void updateActionIcons() {
		View panel = mActionIconPanel;
		if (panel == null) {
			return;
		}
		
		//actions: add, share, remove,
		boolean kb = isKeyguarded();
		
		panel.setVisibility(View.VISIBLE);//panel.setVisibility(kb ? View.INVISIBLE : View.VISIBLE);

		boolean b = hasImage() && !kb; //boolean b = hasImage();

		int vi = (b ? View.VISIBLE : View.INVISIBLE);

		int[] ids = { R.id.remove, R.id.share };
		for (int i = 0; i < ids.length; i++) {
			View v = panel.findViewById(ids[i]);
			v.setEnabled(b);
			v.setVisibility(vi);
		}
		
		View v = panel.findViewById(R.id.add);
		if (!kb) {
			v.setVisibility(View.VISIBLE);
			v.setEnabled(true);
		} else {
			v.setVisibility(View.INVISIBLE);
			v.setEnabled(false);
		}
	}

	private boolean hasImage() {
		boolean b = (mAllImages != null && mAllImages.getCount() > 0);
		return b;
	}

	private void updateNextPrevControls() {
		boolean showPrev = mCurrentPosition > 0;
		boolean showNext = mCurrentPosition < mAllImages.getCount() - 1;

		boolean prevIsVisible = mPrevImageView.getVisibility() == View.VISIBLE;
		boolean nextIsVisible = mNextImageView.getVisibility() == View.VISIBLE;

		if (showPrev && !prevIsVisible) {
			Animation a = mShowPrevImageViewAnimation;
			a.setDuration(500);
			mPrevImageView.startAnimation(a);
			mPrevImageView.setVisibility(View.VISIBLE);
		} else if (!showPrev && prevIsVisible) {
			Animation a = mHidePrevImageViewAnimation;
			a.setDuration(500);
			mPrevImageView.startAnimation(a);
			mPrevImageView.setVisibility(View.GONE);
		}

		if (showNext && !nextIsVisible) {
			Animation a = mShowNextImageViewAnimation;
			a.setDuration(500);
			mNextImageView.startAnimation(a);
			mNextImageView.setVisibility(View.VISIBLE);
		} else if (!showNext && nextIsVisible) {
			Animation a = mHideNextImageViewAnimation;
			a.setDuration(500);
			mNextImageView.startAnimation(a);
			mNextImageView.setVisibility(View.GONE);
		}
	}

	private void updateZoomButtonsEnabled() {
		ImageViewTouch iv = mImageView;
		float scale = iv.getScale();
		ZoomButtonsController c = mZoomButtonsController;
		//
		c.setZoomInEnabled(scale < iv.mMaxZoom);
		c.setZoomOutEnabled(scale > 1);
		//
	}

	private PowerManager.WakeLock mWakeLock;

	private void acquireLock() {
		if (Debug.DEBUG) {
			Log.v(TAG, "Acquiring screen wake lock");
		}
		if (mWakeLock != null) {
			return;
		}
		try {
			PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);

			mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK
					| PowerManager.ACQUIRE_CAUSES_WAKEUP
					| PowerManager.ON_AFTER_RELEASE, TAG);
			
			mWakeLock.acquire();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void releaseLock() {
		if (Debug.DEBUG) {
			Log.v(TAG, "Releasing wake lock");
		}
		//
		if (mWakeLock != null) {
			mWakeLock.release();
		}
		mWakeLock = null;
	}

	public static void ensureRunning(Context ctx) {
		final boolean b = Preferences.getRunningStatus(ctx);		

		if (Debug.DEBUG) {
			Log.d(TAG, "ensureRunning. was running? " + b );
		}
		
		if (b) {
			try {
				Intent ezb = new Intent(ctx, ImageHolderActivity.class);
				ezb.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				ctx.startActivity(ezb);
			} catch (Exception e) {
				if (Debug.DEBUG) {
					e.printStackTrace();
				}
			}
		}
	}
	
	private class MyScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
		@Override
        public boolean onScale(ScaleGestureDetector detector) {
			if (scrollLocked) {
				return false;
			}
        	if (!detector.isInProgress()) {
        		return false;
        	}
        	
        	final float factor = detector.getScaleFactor();
        	float scale = mImageView.getScale();
            scale *= factor;
            scale = Math.max(mImageView.mMinZoom, Math.min(scale, mImageView.mMaxZoom));
        	
        	final float x = detector.getFocusX();
        	final float y = detector.getFocusY();
        	
        	mImageView.zoomTo(scale, x * factor, y * factor);

            return true;
        }
    }

	private void showError(Exception e) {
		Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
	}

	protected void setFullscreen() {
		final Window w = getWindow();
		w.requestFeature(Window.FEATURE_NO_TITLE);
		w.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		w.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
	}
}
